package org.ytu.classifier.fileIO;

import org.ytu.classifier.beans.AnalyseBean;
import org.ytu.classifier.beans.KmeansResultBean;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AlgorithmOutput {
   int k;
   int cepstralCoefficients;
   int numberOfFilters;
   Map[] resultMaps;// Hangi kümede hangi elemandan kaç tane olduğunu tutar
   double[] reconstructionErrors;// Her bir kümenin reconstruction error'ünü tutar
   int animalCount;
   boolean isMel;
   KmeansResultBean algorithmResult;
   List<String> animalList;//Algoritmada kullanılan hayvanların isimlerinin listesi
   PrintWriter writer;
   AnalyseBean analyse;

   public AlgorithmOutput(String logFile) {
      analyse = new AnalyseBean();
      try {
         writer = new PrintWriter(new BufferedWriter(new FileWriter(logFile, true)));
      } catch (IOException e) {
         System.out.println(e.getMessage());
      }
   }

   public AnalyseBean logKmeansResult(KmeansResultBean algorithmResult) {
      initLogVariables(algorithmResult);
      initAnalyse();
      StringBuilder log = new StringBuilder("");

      //Algoritma başlangıç parametreleri
      log.append(isMel ? "MFCC " : "BFCC ");
      log.append("k: ");
      log.append(k);
      log.append(" ");
      log.append("cepstralCoefficients: ");
      log.append(cepstralCoefficients);
      log.append(" ");
      log.append("numberOfFilters: ");
      log.append(numberOfFilters);
      log.append(" ");
      log.append("Ortamdaki Hayvan Sayısı: ");
      log.append(animalCount);
      log.append("\n");

      log.append("\n");
      log.append("Confusion Matrix: ");
      log.append("\n");

      //confusion matrix yazılıyor
      int[][] confusionMatrix = calculateConfusionMatrix();

      for (int i = 0; i < animalList.size(); i++) {
         log.append(animalList.get(i));
         log.append(" ");
         for (int j = 0; j < k; j++) {
            log.append(confusionMatrix[i][j]);
            log.append(" ");
         }
         log.append("\n");
      }

      //Reconstruction error yazılıyor
      double totalReconstructionError = 0.0;
      log.append("\n");
      log.append("Reconstruction Error: \n");
      for (int i = 0; i < k; i++) {
         log.append("Küme ");
         log.append(i);
         log.append(": ");
         log.append(reconstructionErrors[i]);
         log.append("\n");
         totalReconstructionError += reconstructionErrors[i];
      }

      analyse.setTotalError(totalReconstructionError);

      //Kümeleme başarısı yazılıyor
      Map<Integer, Double> successMap = new HashMap<Integer, Double>();
      double clusterSuccessRate;
      int totalFrame;
      //List<Integer> differentSpecies = new ArrayList<Integer>();
      for (int i = 0; i < animalList.size(); i++) {
         totalFrame = 0;
         Integer cluster = null;
         int tempCount = -1;
         int clusterIndex = -1;
         for (int j = 0; j < k; j++) {
            totalFrame += confusionMatrix[i][j];
            if (confusionMatrix[i][j] > tempCount) {
               tempCount = confusionMatrix[i][j];
               clusterIndex = j;
            }
         }
         clusterSuccessRate = tempCount * 1.0 / totalFrame;
         cluster = clusterIndex;
         if (!successMap.containsKey(cluster)) {
            successMap.put(cluster, clusterSuccessRate);
         } else {// Başarı oranı en yüksek olan alınacak
            double previousSucessRate = successMap.get(cluster);
            if (clusterSuccessRate > previousSucessRate) {
               successMap.remove(cluster);
               successMap.put(cluster, clusterSuccessRate);
            }
         }
         /*
         if (!differentSpecies.contains(cluster)) {
            differentSpecies.add(cluster);
         }
         */
      }

      //double successRate = differentSpecies.size() * 1.0 / k;
      double totalSuccessRate = 0.0;

      for(Map.Entry<Integer,Double> me : successMap.entrySet()){
         totalSuccessRate += me.getValue();
      }
      analyse.setClusterSuccessRate(totalSuccessRate);

      log.append("\n");
      log.append("Kümeleme Başarısı: ");
      log.append(totalSuccessRate);
      log.append("\n");


      //Toplam Reconstruction Error yazılıyor
      log.append("Toplam Reconstruction Error: ");
      log.append(totalReconstructionError);
      log.append("\n");

      log.append("**************************************");
      writer.println(log.toString());
      writer.close();
      return analyse;
   }

   public List<String> getAnimalNames() {
      List<String> animalList = new ArrayList<String>();
      for (Map<String, Integer> resultMap : resultMaps) {
         for (Map.Entry<String, Integer> clusterResult : resultMap.entrySet()) {
            if (!animalList.contains(clusterResult.getKey())) {
               animalList.add(clusterResult.getKey());
            }
         }
      }
      return animalList;
   }

   public int[][] calculateConfusionMatrix() {
      int[][] confusionMatrix = new int[animalList.size()][k];

      for (int i = 0; i < animalList.size(); i++) {
         String animalName = animalList.get(i);
         for (int j = 0; j < k; j++) {
            Map<String, Integer> resultMap = resultMaps[j];
            if (resultMap.containsKey(animalName)) {
               confusionMatrix[i][j] = resultMap.get(animalName);
            } else {
               confusionMatrix[i][j] = 0;
            }
         }
      }
      return confusionMatrix;
   }

   public void initLogVariables(KmeansResultBean algorithmResult) {
      this.algorithmResult = algorithmResult;
      this.k = algorithmResult.getK();
      this.cepstralCoefficients = algorithmResult.getCepstralCoefficients();
      this.numberOfFilters = algorithmResult.getNumberOfFilters();
      this.resultMaps = algorithmResult.getResultMaps();
      this.reconstructionErrors = algorithmResult.getReconstructionErrors();
      this.animalCount = algorithmResult.getAnimalCount();
      this.isMel = algorithmResult.isMel();
      animalList = getAnimalNames();
   }
   public void initAnalyse(){
      analyse.setK(this.k);
      analyse.setCepstralCoefficients(this.cepstralCoefficients);
      analyse.setNumberOfFilters(this.numberOfFilters);
      analyse.setMel(this.isMel);
      analyse.setAnimalCount(animalCount);
   }
}
